package com.stone.framework.core.util;

import java.beans.PropertyDescriptor;
import java.time.LocalDateTime;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import javax.persistence.EntityListeners;

import org.springframework.beans.BeanUtils;
import org.springframework.beans.BeanWrapper;
import org.springframework.beans.BeanWrapperImpl;

import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.stone.framework.core.context.UserContext;
import com.stone.framework.core.entity.CommonEntity;
import com.stone.framework.core.entity.DebtEntity;

public final class Beans {

    private static ObjectMapper mapper = new ObjectMapper();

    public static <T> String beanToString(T obj) {
        try {
            return mapper.writeValueAsString(obj);
        } catch (JsonProcessingException e) {
            return null;
        }
    }

    public static <T> Map<String, Object> beanToMap(T obj) {
        try {
            JavaType javaType =
                mapper.getTypeFactory().constructMapType(LinkedHashMap.class, String.class, Object.class);
            return mapper.readValue(beanToString(obj), javaType);
        } catch (JsonProcessingException e) {
            return null;
        }
    }

    public static <S extends DebtEntity, T extends DebtEntity> T copyDebtProperties(S source, T target) {
        source.setOrgCode(target.getOrgCode());
        source.setProjectCode(target.getProjectCode());
        source.setEventCode(target.getEventCode());
        source.setBatchCode(target.getBatchCode());
        return target;
    }

    /**
     * 实现功能同 mybatis {@link MetaObjectHandler} / jpa {@link EntityListeners}
     * 
     * @param entity
     * @return
     */
    public static <S extends CommonEntity> S buildCommonEntity(S entity) {
        // entity.setId(entity.getId() == null ? UUID.randomUUID().toString().replace("-", "") : entity.getId());
        entity.setVersion(entity.getVersion() == null ? 0L : entity.getVersion() + 1);

        LocalDateTime now = LocalDateTime.now();
        String username = UserContext.getDefaultUsername();
        if (entity.getCreateDate() == null) {
            entity.setCreateDate(now);
            entity.setCreateUser(username);
        } else {
            entity.setUpdateDate(now);
            entity.setUpdateUser(username);
        }
        return entity;
    }

    public static boolean notBlank(String obj) {
        return obj != null && !obj.trim().isEmpty();
    }

    public static <S, T> void copyProperties(S source, T target) {
        BeanUtils.copyProperties(source, target, getNullProperties(source));
    }

    public static <S, T> List<T> copyListProperties(List<S> sources, Class<T> clazz) {

        return sources.stream().map(source -> {
            T target = null;
            try {
                target = clazz.newInstance();
            } catch (InstantiationException | IllegalAccessException e) {
            }
            copyProperties(source, target);
            return target;
        }).collect(Collectors.toList());
    }

    private static String[] getNullProperties(Object source) {
        BeanWrapper wrapper = new BeanWrapperImpl(source);
        PropertyDescriptor[] descriptor = wrapper.getPropertyDescriptors();
        return Stream.of(descriptor).filter(prop -> wrapper.getPropertyValue(prop.getName()) == null)
            .map(prop -> prop.getName()).toArray(String[]::new);
    }
}
